package com.googlecode.recycled.data;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.googlecode.recycled.lang.assertions.Assert;
import smokesignals.data.QueryHandler;

import java.util.List;
import java.util.Map;

public abstract class AbstractDataStore<T> implements DataStore<T> {

    protected List<DataStoreEventListener<T>> dataStoreEventListeners = Lists.newLinkedList();

    protected Map<Class, QueryHandler> queryHandlers = Maps.newHashMap();

    @Override
    public void delete(WithId<T> withId) {
        Assert.notNull(withId);

        for (DataStoreEventListener<T> dataStoreEventListener : dataStoreEventListeners) {
            dataStoreEventListener.beforeDelete(withId);
        }
        doDelete(withId);
        for (DataStoreEventListener<T> dataStoreEventListener : dataStoreEventListeners) {
            dataStoreEventListener.afterDelete(withId);
        }
    }

    protected abstract void doDelete(WithId<T> withId);

    @Override
    public <V extends WithId<T>> V load(Class<V> clazz, T id) {
        return doLoad(clazz, id);
    }

    protected abstract <V extends WithId<T>> V doLoad(Class<V> clazz, T id);

    @Override
    public void store(WithId<T> withId) {
        Assert.notNull(withId);

        for (DataStoreEventListener<T> dataStoreEventListener : dataStoreEventListeners) {
            dataStoreEventListener.beforeStore(withId);
        }
        doStore(withId);
        for (DataStoreEventListener<T> dataStoreEventListener : dataStoreEventListeners) {
            dataStoreEventListener.afterStore(withId);
        }
    }

    protected abstract void doStore(WithId<T> withId);

    public void addPersisterListener(DataStoreEventListener<T> dataStoreEventListener) {
        dataStoreEventListeners.add(dataStoreEventListener);
    }

    @Override
    public void close() {
    }

    @Override
    @SuppressWarnings("unchecked") // Raw QueryHandler vs parametrized Q
    public <Q, R> List<R> query(Q query, Object... parameters) {
        for (Map.Entry<Class, QueryHandler> classHandlerPair : queryHandlers.entrySet()) {
            if (classHandlerPair.getKey().isAssignableFrom(query.getClass())) {
                return classHandlerPair.getValue().handle(query, parameters);
            }
        }
        throw new RuntimeException("No handler for query type " + query.getClass().getName());
    }

    public void registerQuerySupport(Class queryType, QueryHandler queryHandler) {
        queryHandlers.put(queryType, queryHandler);
    }

}
